#ifndef __ATLGDIX_H__
#define __ATLGDIX_H__

/////////////////////////////////////////////////////////////////////////////
// Additional GDI/USER wrappers
//
// Written by Bjarke Viksoe (bjarke@viksoe.dk)
// Copyright (c) 2001-2002 Bjarke Viksoe.
// Thanks to Daniel Bowen for COffscreenDrawRect.
//
// This code may be used in compiled form in any way you desire. This
// file may be redistributed by any means PROVIDING it is
// not sold for profit without the authors written consent, and
// providing that this notice and the authors name is included.
//
// This file is provided "as is" with no expressed or implied warranty.
// The author accepts no liability if it causes any damage to you or your
// computer whatsoever. It's free, so don't hassle me about it.
//
// Beware of bugs.
//

#pragma once

#ifndef __cplusplus
#error ATL requires C++ compilation (use a .cpp suffix)
#endif

#ifndef __ATLGDI_H__
#error atlgdix.h requires atlgdi.h to be included first
#endif

namespace WTL {

/////////////////////////////////////////////////////////////////////////////
// Macros

// The GetXValue macros below are badly designed and emit
// compiler warnings e.g. when using RGB(255,255,255)...
#pragma warning(disable : 4310)

#ifndef BlendRGB
#define BlendRGB(c1, c2, factor)                                           \
	RGB(GetRValue(c1) + ((GetRValue(c2) - GetRValue(c1)) * factor / 100L), \
		GetGValue(c1) + ((GetGValue(c2) - GetGValue(c1)) * factor / 100L), \
		GetBValue(c1) + ((GetBValue(c2) - GetBValue(c1)) * factor / 100L))
#endif

#ifndef COLOR_INVALID
#define COLOR_INVALID (COLORREF) CLR_INVALID
#endif


#if _WTL_VER < 0x0750

/////////////////////////////////////////////////////////////////////////////
// CIcon

template <bool t_bManaged>
class CIconT
{
public:
	HICON m_hIcon;

	// Constructor/destructor/operators

	CIconT(HICON hIcon = NULL) :
		m_hIcon(hIcon)
	{
	}

	~CIconT()
	{
		if (t_bManaged && this->m_hIcon != NULL)
			::DestroyIcon(m_hIcon);
	}

	CIconT<t_bManaged>& operator=(HICON hIcon)
	{
		m_hIcon = hIcon;
		return *this;
	}

	void Attach(HICON hIcon)
	{
		if (t_bManaged && this->m_hIcon != NULL)
			::DestroyIcon(m_hIcon);
		m_hIcon = hIcon;
	}
	HICON Detach()
	{
		HICON hIcon = this->m_hIcon;
		m_hIcon = NULL;
		return hIcon;
	}

	operator HICON() const { return this->m_hIcon; }

	bool IsNull() const { return this->m_hIcon == NULL; }

	// Create methods

	HICON LoadIcon(_U_STRINGorID icon)
	{
		ATLASSERT(m_hIcon == NULL);
#if (_ATL_VER >= 0x0700)
		m_hIcon = ::LoadIcon(ATL::_AtlBaseModule.GetResourceInstance(), icon.m_lpstr);
#else
		m_hIcon = ::LoadIcon(_Module.GetResourceInstance(), icon.m_lpstr);
#endif
		return this->m_hIcon;
	}
	HICON LoadIcon(_U_STRINGorID icon, int cxDesired, int cyDesired, UINT fuLoad = 0)
	{
		ATLASSERT(m_hIcon == NULL);
#if (_ATL_VER >= 0x0700)
		m_hIcon = (HICON)::LoadImage(ATL::_AtlBaseModule.GetResourceInstance(), icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad);
#else
		m_hIcon = (HICON)::LoadImage(_Module.GetResourceInstance(), icon.m_lpstr, IMAGE_ICON, cxDesired, cyDesired, fuLoad);
#endif
		return this->m_hIcon;
	}
	HICON LoadOEMIcon(UINT nIDIcon) // for IDI_ types
	{
		ATLASSERT(m_hIcon == NULL);
		m_hIcon = ::LoadIcon(NULL, MAKEINTRESOURCE(nIDIcon));
		return this->m_hIcon;
	}
	HICON CreateIcon(int nWidth, int nHeight, BYTE cPlanes, BYTE cBitsPixel, CONST BYTE* lpbANDButs, CONST BYTE* lpbXORbits)
	{
		ATLASSERT(m_hIcon == NULL);
		ATLASSERT(lpbANDbits);
		ATLASSERT(lpbXORbits);
#if (_ATL_VER >= 0x0700)
		m_hIcon = ::CreateIcon(ATL::_AtlBaseModule.GetResourceInstance(), nWidth, nHeight, cPlanes, cBitsPixel, lpbANDbits, lpbXORbits);
#else
		m_hIcon = ::CreateIcon(_Module.GetResourceInstance(), nWidth, nHeight, cPlanes, cBitsPixel, lpbANDbits, lpbXORbits);
#endif
		return this->m_hIcon;
	}
	HICON CreateIconFromResource(PBYTE pBits, DWORD dwResSize, DWORD dwVersion = 0x00030000)
	{
		ATLASSERT(m_hIcon == NULL);
		ATLASSERT(pBits);
		m_hIcon = ::CreateIconFromResource(pBits, dwResSize, TRUE, dwVersion);
		return this->m_hIcon;
	}
	HICON CreateIconFromResourceEx(PBYTE pbBits, DWORD cbBits, DWORD dwVersion = 0x00030000, int cxDesired = 0, int cyDesired = 0, UINT uFlags = LR_DEFAULTCOLOR)
	{
		ATLASSERT(m_hIcon == NULL);
		ATLASSERT(pbBits);
		ATLASSERT(cbBits > 0);
		m_hIcon = ::CreateIconFromResourceEx(pbBits, cbBits, TRUE, dwVersion, cxDesired, cyDesired, uFlags);
		return this->m_hIcon;
	}
	HICON CreateIconIndirect(PICONINFO pIconInfo)
	{
		ATLASSERT(m_hIcon == NULL);
		ATLASSERT(pIconInfo);
		m_hIcon = ::CreateIconIndirect(pIconInfo);
		return this->m_hIcon;
	}
	HICON ExtractIcon(LPCTSTR lpszExeFileName, UINT nIconIndex)
	{
		ATLASSERT(m_hIcon == NULL);
		ATLASSERT(!::IsBadStringPtr(lpszExeFileName, -1));
#if (_ATL_VER >= 0x0700)
		m_hIcon = ::ExtractIcon(ATL::_AtlBaseModule.GetModuleInstance(), lpszExeFileName, nIconIndex);
#else
		m_hIcon = ::ExtractIcon(_Module.GetModuleInstance(), lpszExeFileName, nIconIndex);
#endif
		return this->m_hIcon;
	}
	HICON ExtractAssociatedIcon(HINSTANCE hInst, LPCTSTR lpIconPath, LPWORD lpiIcon)
	{
		ATLASSERT(m_hIcon == NULL);
		ATLASSERT(!::IsBadStringPtr(lpIconPath, -1));
		ATLASSERT(lpiIcon);
		m_hIcon = ::ExtractAssociatedIcon(hInst, lpIconPath, lpiIcon);
		return this->m_hIcon;
	}

	// Operations

	BOOL DestroyIcon()
	{
		ATLASSERT(m_hIcon != NULL);
		BOOL bRet = ::DestroyIcon(m_hIcon);
		if (bRet)
			m_hIcon = NULL;
		return bRet;
	}
	HICON CopyIcon()
	{
		ATLASSERT(m_hIcon != NULL);
		return ::CopyIcon(m_hIcon);
	}
	HICON DuplicateIcon()
	{
		ATLASSERT(m_hIcon != NULL);
		return ::DuplicateIcon(NULL, this->m_hIcon);
	}

	BOOL DrawIcon(HDC hDC, int x, int y)
	{
		ATLASSERT(m_hIcon != NULL);
		return ::DrawIcon(hDC, x, y, this->m_hIcon);
	}
	BOOL DrawIcon(HDC hDC, POINT pt)
	{
		ATLASSERT(m_hIcon != NULL);
		return ::DrawIcon(hDC, pt.x, pt.y, this->m_hIcon);
	}
	BOOL DrawIconEx(HDC hDC, int x, int y, int cxWidth, int cyWidth, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL)
	{
		ATLASSERT(m_hIcon != NULL);
		return ::DrawIconEx(hDC, x, y, this->m_hIcon, cxWidth, cyWidth, uStepIfAniCur, hbrFlickerFreeDraw, uFlags);
	}
	BOOL DrawIconEx(HDC hDC, POINT pt, SIZE size, UINT uStepIfAniCur = 0, HBRUSH hbrFlickerFreeDraw = NULL, UINT uFlags = DI_NORMAL)
	{
		ATLASSERT(m_hIcon != NULL);
		return ::DrawIconEx(hDC, pt.x, pt.y, this->m_hIcon, size.cx, size.cy, uStepIfAniCur, hbrFlickerFreeDraw, uFlags);
	}

	BOOL GetIconInfo(PICONINFO pIconInfo)
	{
		ATLASSERT(m_hIcon != NULL);
		ATLASSERT(pIconInfo);
		return ::GetIconInfo(m_hIcon, pIconInfo);
	}
};

typedef CIconT<true> CIcon;
typedef CIconT<false> CIconHandle;


/////////////////////////////////////////////////////////////////////////////
// CCursor

// Protect template against silly macro
#ifdef CopyCursor
#undef CopyCursor
#endif

template <bool t_bManaged>
class CCursorT
{
public:
	HCURSOR m_hCursor;

	// Constructor/destructor/operators

	CCursorT(HCURSOR hCursor = NULL) :
		m_hCursor(hCursor)
	{
	}

	~CCursorT()
	{
		if (t_bManaged && this->m_hCursor != NULL)
			::DestroyCursor(m_hCursor);
	}

	CCursorT<t_bManaged>& operator=(HCURSOR hCursor)
	{
		m_hCursor = hCursor;
		return *this;
	}

	void Attach(HCURSOR hCursor)
	{
		if (t_bManaged && this->m_hCursor != NULL)
			::DestroyCursor(m_hCursor);
		m_hCursor = hCursor;
	}
	HCURSOR Detach()
	{
		HCURSOR hCursor = this->m_hCursor;
		m_hCursor = NULL;
		return hCursor;
	}

	operator HCURSOR() const { return this->m_hCursor; }

	bool IsNull() const { return this->m_hCursor == NULL; }

	// Create methods

	HCURSOR LoadCursor(_U_STRINGorID cursor)
	{
		ATLASSERT(m_hCursor == NULL);
#if (_ATL_VER >= 0x0700)
		m_hCursor = ::LoadCursor(ATL::_AtlBaseModule.GetResourceInstance(), cursor.m_lpstr);
#else
		m_hCursor = ::LoadCursor(_Module.GetResourceInstance(), cursor.m_lpstr);
#endif
		return this->m_hCursor;
	}
	HCURSOR LoadOEMCursor(UINT nIDCursor) // for IDC_ types
	{
		ATLASSERT(m_hCursor == NULL);
		m_hCursor = ::LoadCursor(NULL, MAKEINTRESOURCE(nIDCursor));
		return this->m_hCursor;
	}
	HICON LoadCursor(_U_STRINGorID cursor, int cxDesired, int cyDesired, UINT fuLoad = 0)
	{
		ATLASSERT(m_hCursor == NULL);
#if (_ATL_VER >= 0x0700)
		m_hCursor = (HCURSOR)::LoadImage(ATL::_AtlBaseModule.GetResourceInstance(), cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad);
#else
		m_hCursor = (HCURSOR)::LoadImage(_Module.GetResourceInstance(), cursor.m_lpstr, IMAGE_CURSOR, cxDesired, cyDesired, fuLoad);
#endif
		return this->m_hCursor;
	}
	HCURSOR LoadCursorFromFile(LPCTSTR pstrFilename)
	{
		ATLASSERT(m_hCursor == NULL);
		ATLASSERT(!::IsBadStringPtr(pstrFilename, -1));
		m_hCursor = ::LoadCursorFromFile(pstrFilename);
		return this->m_hCursor;
	}
	HCURSOR CreateCursor(int xHotSpot, int yHotSpot, int nWidth, int nHeight, CONST VOID* pvANDPlane, CONST VOID* pvXORPlane)
	{
		ATLASSERT(m_hCursor == NULL);
#if (_ATL_VER >= 0x0700)
		m_hCursor = ::CreateCursor(ATL::_AtlBaseModule.GetResourceInstance(), xHotSpot, yHotSpot, nWidth, nHeight, pvANDPlane, pvXORPlane);
#else
		m_hCursor = ::CreateCursor(_Module.GetResourceInstance(), xHotSpot, yHotSpot, nWidth, nHeight, pvANDPlane, pvXORPlane);
#endif
		return this->m_hCursor;
	}
	HICON CreateCursorFromResource(PBYTE pBits, DWORD dwResSize, DWORD dwVersion = 0x00030000)
	{
		ATLASSERT(m_hIcon == NULL);
		ATLASSERT(pBits);
		m_hIcon = ::CreateIconFromResource(pBits, dwResSize, FALSE, dwVersion);
		return this->m_hIcon;
	}
	HICON CreateCursorFromResourceEx(PBYTE pbBits, DWORD cbBits, DWORD dwVersion = 0x00030000, int cxDesired = 0, int cyDesired = 0, UINT uFlags = LR_DEFAULTCOLOR)
	{
		ATLASSERT(m_hIcon == NULL);
		ATLASSERT(pbBits);
		ATLASSERT(cbBits > 0);
		m_hIcon = ::CreateIconFromResourceEx(pbBits, cbBits, FALSE, dwVersion, cxDesired, cyDesired, uFlags);
		return this->m_hIcon;
	}

	// Operations

	BOOL DestroyCursor()
	{
		ATLASSERT(m_hCursor != NULL);
		BOOL bRet = ::DestroyCursor(m_hCursor);
		if (bRet)
			m_hCursor = NULL;
		return bRet;
	}

	HCURSOR CopyCursor()
	{
		ATLASSERT(m_hCursor != NULL);
		return (HCURSOR)::CopyIcon((HICON)m_hCursor);
	}

#if (WINVER >= 0x0500)
	BOOL GetCursorInfo(LPCURSORINFO pCursorInfo)
	{
		ATLASSERT(m_hCursor != NULL);
		ATLASSERT(pCursorInfo);
		return ::GetCursorInfo(pCursorInfo);
	}
#endif
};

typedef CCursorT<true> CCursor;
typedef CCursorT<false> CCursorHandle;


/////////////////////////////////////////////////////////////////////////////
// CAccelerator

template <bool t_bManaged>
class CAcceleratorT
{
public:
	HACCEL m_hAccel;

	// Constructor/destructor/operators

	CAcceleratorT(HACCEL hAccel = NULL) :
		m_hAccel(hAccel)
	{
	}

	~CAcceleratorT()
	{
		if (t_bManaged && this->m_hAccel != NULL)
			::DestroyAcceleratorTable(m_hAccel);
	}

	CAcceleratorT<t_bManaged>& operator=(HACCEL hAccel)
	{
		m_hAccel = hAccel;
		return *this;
	}

	void DestroyObject()
	{
		if (m_hAccel != NULL)
		{
			::DestroyAcceleratorTable(m_hAccel);
			m_hAccel = NULL;
		}
	}

	void Attach(HACCEL hAccel)
	{
		if (t_bManaged && this->m_hAccel != NULL)
			::DestroyAcceleratorTable(m_hAccel);
		m_hAccel = hAccel;
	}
	HCURSOR Detach()
	{
		HACCEL hAccel = this->m_hAccel;
		m_hAccel = NULL;
		return hAccel;
	}

	operator HACCEL() const { return this->m_hAccel; }

	bool IsNull() const { return this->m_hAccel == NULL; }

	// Create methods

	HACCEL LoadAccelerators(_U_STRINGorID accel)
	{
		ATLASSERT(m_hAccel == NULL);
#if (_ATL_VER >= 0x0700)
		m_hAccel = ::LoadAccelerators(ATL::_AtlBaseModule.GetResourceInstance(), accel.m_lpstr);
#else
		m_hAccel = ::LoadAccelerators(_Module.GetResourceInstance(), accel.m_lpstr);
#endif
		return this->m_hAccel;
	}
	HACCEL CreateAcceleratorTable(LPACCEL pAccel, int cEntries)
	{
		ATLASSERT(m_hAccel == NULL);
		ATLASSERT(!::IsBadReadPtr(lpAccelDst, sizeof(ACCEL) * cEntries));
		m_hAccel = ::CreateAcceleratorTable(pAccel, cEntries);
		return this->m_hAccel;
	}

	// Operations

	int CopyAcceleratorTable(LPACCEL lpAccelDst, int cEntries)
	{
		ATLASSERT(m_hAccel != NULL);
		ATLASSERT(!::IsBadWritePtr(lpAccelDst, sizeof(ACCEL) * cEntries));
		return ::CopyAcceleratorTable(m_hAccel, lpAccelDst, cEntries);
	}

	BOOL TranslateAccelerator(HWND hWnd, LPMSG pMsg)
	{
		ATLASSERT(m_hAccel != NULL);
		ATLASSERT(::IsWindow(hWnd));
		ATLASSERT(pMsg);
		return ::TranslateAccelerator(hWnd, this->m_hAccel, pMsg);
	}
};

typedef CAcceleratorT<true> CAccelerator;
typedef CAcceleratorT<false> CAcceleratorHandle;


/////////////////////////////////////////////////////////////////////////////
// CLogFont

class CLogFont : public LOGFONT
{
public:
	CLogFont()
	{
		::ZeroMemory((LOGFONT*)this, sizeof(LOGFONT));
	}
	CLogFont(const LOGFONT& lf)
	{
		Copy(&lf);
	}
	CLogFont(HFONT hFont)
	{
		ATLASSERT(::GetObjectType(hFont) == OBJ_FONT);
		::GetObject(hFont, sizeof(LOGFONT), (LOGFONT*)this);
	}
	HFONT CreateFontIndirect()
	{
		return ::CreateFontIndirect(this);
	}
	void SetBold()
	{
		lfWeight = FW_BOLD;
	}
	BOOL IsBold() const
	{
		return lfWeight >= FW_BOLD;
	}
	void MakeBolder(int iScale = 1)
	{
		lfWeight += FW_BOLD * iScale;
	}
	void MakeLarger(int iScale)
	{
		if (lfHeight > 0)
			lfHeight += iScale;
		else
			lfHeight -= iScale;
	}
	void SetHeight(long PointSize, HDC hDC = NULL)
	{
		// For MM_TEXT mapping mode...
		// NOTE: MulDiv() gives correct rounding.
		lfHeight = -MulDiv(PointSize, ::GetDeviceCaps(hDC, LOGPIXELSY), 72);
	}
	long GetHeight(HDC hDC = NULL) const
	{
		// For MM_TEXT mapping mode...
		// NOTE: MulDiv() gives correct rounding.
		return MulDiv(-lfHeight, 72, ::GetDeviceCaps(hDC, LOGPIXELSY));
	}
	long GetDeciPointHeight(HDC hDC = NULL)
	{
		POINT ptOrg = {0, 0};
		::DPtoLP(hDC, &ptOrg, 1);
		POINT pt = {0, 0};
		pt.y = abs(lfHeight) + ptOrg.y;
		::LPtoDP(hDC, &pt, 1);
		return MulDiv(pt.y, 720, ::GetDeviceCaps(hDC, LOGPIXELSY)); // 72 points/inch, 10 decipoints/point
	}
	void SetHeightFromDeciPoint(long DeciPtHeight, HDC hDC = NULL)
	{
		POINT pt;
		pt.y = MulDiv(::GetDeviceCaps(hDC, LOGPIXELSY), DeciPtHeight, 720); // 72 points/inch, 10 decipoints/point
		::DPtoLP(hDC, &pt, 1);
		POINT ptOrg = {0, 0};
		::DPtoLP(hDC, &ptOrg, 1);
		lfHeight = -abs(pt.y - ptOrg.y);
	}
	void SetCaptionFont()
	{
		NONCLIENTMETRICS ncm = {0};
		ncm.cbSize = sizeof(ncm);
		::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
		Copy(&ncm.lfCaptionFont);
	}
	void SetMenuFont()
	{
		NONCLIENTMETRICS ncm = {0};
		ncm.cbSize = sizeof(ncm);
		::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
		Copy(&ncm.lfMenuFont);
	}
	void SetStatusFont()
	{
		NONCLIENTMETRICS ncm = {0};
		ncm.cbSize = sizeof(ncm);
		::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
		Copy(&ncm.lfStatusFont);
	}
	void SetMessageBoxFont()
	{
		NONCLIENTMETRICS ncm = {0};
		ncm.cbSize = sizeof(ncm);
		::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
		Copy(&ncm.lfMessageFont);
	}
	void Copy(const LOGFONT* lf)
	{
		ATLASSERT(lf);
		::CopyMemory((LOGFONT*)this, lf, sizeof(LOGFONT));
	}
	CLogFont& operator=(const CLogFont& src)
	{
		Copy(&src);
		return *this;
	}
	CLogFont& operator=(const LOGFONT& src)
	{
		Copy(&src);
		return *this;
	}
	CLogFont& operator=(HFONT hFont)
	{
		ATLASSERT(::GetObjectType(hFont) == OBJ_FONT);
		::GetObject(hFont, sizeof(LOGFONT), (LOGFONT*)this);
		return *this;
	}
	bool operator==(const LOGFONT& logfont) const
	{
		return (logfont.lfHeight == lfHeight &&
				logfont.lfWidth == lfWidth &&
				logfont.lfEscapement == lfEscapement &&
				logfont.lfOrientation == lfOrientation &&
				logfont.lfWeight == lfWeight &&
				logfont.lfItalic == lfItalic &&
				logfont.lfUnderline == lfUnderline &&
				logfont.lfStrikeOut == lfStrikeOut &&
				logfont.lfCharSet == lfCharSet &&
				logfont.lfOutPrecision == lfOutPrecision &&
				logfont.lfClipPrecision == lfClipPrecision &&
				logfont.lfQuality == lfQuality &&
				logfont.lfPitchAndFamily == lfPitchAndFamily &&
				::lstrcmp(logfont.lfFaceName, lfFaceName) == 0);
	}
};

#endif // _WTL_VER


/////////////////////////////////////////////////////////////////////////////
// CMemDC

class CMemDC : public CDC
{
public:
	CDCHandle m_dc; // Owner DC
	CBitmap m_bitmap; // Offscreen bitmap
	CBitmapHandle m_hOldBitmap; // Originally selected bitmap
	RECT m_rc; // Rectangle of drawing area

	CMemDC(HDC hDC, LPRECT pRect = NULL)
	{
		ATLASSERT(hDC != NULL);
		m_dc = hDC;
		if (pRect != NULL)
			m_rc = *pRect;
		else
			m_dc.GetClipBox(&this->m_rc);

		CreateCompatibleDC(m_dc);
		::LPtoDP(m_dc, (LPPOINT)&this->m_rc, sizeof(RECT) / sizeof(POINT));
		m_bitmap.CreateCompatibleBitmap(m_dc, this->m_rc.right - this->m_rc.left, this->m_rc.bottom - this->m_rc.top);
		m_hOldBitmap = SelectBitmap(m_bitmap);
		::DPtoLP(m_dc, (LPPOINT)&this->m_rc, sizeof(RECT) / sizeof(POINT));
		SetWindowOrg(m_rc.left, this->m_rc.top);
	}
	~CMemDC()
	{
		// Copy the offscreen bitmap onto the screen.
		m_dc.BitBlt(m_rc.left, this->m_rc.top, this->m_rc.right - this->m_rc.left, this->m_rc.bottom - this->m_rc.top,
			m_hDC, this->m_rc.left, this->m_rc.top, SRCCOPY);
		// Swap back the original bitmap.
		SelectBitmap(m_hOldBitmap);
	}
};


/////////////////////////////////////////////////////////////////////////////
// COffscreenDraw

// To use it, derive from it and chain it in the message map.
template <class T>
class COffscreenDraw
{
public:
	BEGIN_MSG_MAP(COffscreenDraw)
		MESSAGE_HANDLER(WM_PAINT, OnPaint)
		MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
		MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
	END_MSG_MAP()

	LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		T* pT = static_cast<T*>(this);
		if (wParam != NULL)
		{
			CMemDC memdc((HDC)wParam, NULL);
			pT->DoPaint(memdc.m_hDC);
		}
		else
		{
			RECT rc;
			pT->GetClientRect(&rc);
			CPaintDC dc(pT->m_hWnd);
			CMemDC memdc(dc.m_hDC, &rc);
			pT->DoPaint(memdc.m_hDC);
		}
		return 0;
	}
	LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		return 1; // handled; no need to erase background; do it in DoPaint();
	}
	void DoPaint(CDCHandle dc)
	{
		ATLASSERT(false); // must override this
	}
};

// To use it, derive from it and chain it in the message map.
template <class T>
class COffscreenDrawRect
{
public:
	BEGIN_MSG_MAP(COffscreenDrawRect)
		MESSAGE_HANDLER(WM_PAINT, OnPaint)
		MESSAGE_HANDLER(WM_PRINTCLIENT, OnPaint)
		MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBackground)
	END_MSG_MAP()

	LRESULT OnPaint(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		T* pT = static_cast<T*>(this);
		if (wParam != NULL)
		{
			CMemDC memdc((HDC)wParam, NULL);
			pT->DoPaint(memdc.m_hDC, memdc.m_rc);
		}
		else
		{
			CPaintDC dc(pT->m_hWnd);
			CMemDC memdc(dc.m_hDC, &dc.m_ps.rcPaint);
			pT->DoPaint(memdc.m_hDC, dc.m_ps.rcPaint);
		}
		return 0;
	}
	LRESULT OnEraseBackground(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
	{
		return 1; // handled; no need to erase background; do it in DoPaint();
	}
	void DoPaint(CDCHandle dc, RECT& rcClip)
	{
		ATLASSERT(false); // must override this
	}
};


/////////////////////////////////////////////////////////////////////////////
// CSaveDC

class CSaveDC
{
public:
	HDC m_hDC;
	int m_iState;

	CSaveDC(HDC hDC) :
		m_hDC(hDC)
	{
		ATLASSERT(::GetObjectType(m_hDC) == OBJ_DC || ::GetObjectType(m_hDC) == OBJ_MEMDC);
		m_iState = ::SaveDC(hDC);
		ATLASSERT(m_iState != 0);
	}
	~CSaveDC()
	{
		Restore();
	}
	void Restore()
	{
		if (m_iState == 0)
			return;
		ATLASSERT(::GetObjectType(m_hDC) == OBJ_DC || ::GetObjectType(m_hDC) == OBJ_MEMDC);
		::RestoreDC(m_hDC, this->m_iState);
		m_iState = 0;
	}
};


/////////////////////////////////////////////////////////////////////////////
// CHandle

#if (_ATL_VER < 0x0700)

class CHandle
{
public:
	HANDLE m_h;

	CHandle(HANDLE hSrc = INVALID_HANDLE_VALUE) :
		m_h(hSrc)
	{
	}

	~CHandle()
	{
		Close();
	}

	operator HANDLE() const { return this->m_h; };

	LPHANDLE operator&()
	{
		ATLASSERT(!IsValid());
		return &m_h;
	}

	CHandle& operator=(HANDLE h)
	{
		ATLASSERT(!IsValid());
		m_h = h;
		return *this;
	}

	bool IsValid() const { return this->m_h != INVALID_HANDLE_VALUE; };

	void Attach(HANDLE h)
	{
		if (IsValid())
			::CloseHandle(m_h);
		m_h = h;
	}
	HANDLE Detach()
	{
		HANDLE h = this->m_h;
		m_h = INVALID_HANDLE_VALUE;
		return h;
	}

	BOOL Close()
	{
		BOOL bRes = FALSE;
		if (m_h != INVALID_HANDLE_VALUE)
		{
			bRes = ::CloseHandle(m_h);
			m_h = INVALID_HANDLE_VALUE;
		}
		return bRes;
	}

	BOOL Duplicate(HANDLE hSource, bool bInherit = false)
	{
		ATLASSERT(!IsValid());
		HANDLE hOurProcess = ::GetCurrentProcess();
		BOOL b = ::DuplicateHandle(hOurProcess,
			hSource,
			hOurProcess,
			&m_h,
			DUPLICATE_SAME_ACCESS,
			bInherit,
			DUPLICATE_SAME_ACCESS);
		ATLASSERT(b);
		return b;
	}
};

#endif // _ATL_VER


/////////////////////////////////////////////////////////////////////////////
// Mouse Hover helper

#ifndef NOTRACKMOUSEEVENT

#ifndef WM_MOUSEENTER
#define WM_MOUSEENTER WM_USER + 253
#endif // WM_MOUSEENTER

// To use it, derive from it and chain it in the message map.
// Make sure to set bHandled to FALSE when handling WM_MOUSEMOVE or
// the WM_MOUSELEAVE message!
template <class T>
class CMouseHover
{
public:
	bool m_fMouseOver; // Internal mouse-over state
	bool m_fMouseForceUpdate; // Update window immediately on event

	CMouseHover() :
		m_fMouseOver(false),
		m_fMouseForceUpdate(true)
	{
	}

	void SetForceMouseOverUpdate(bool bForce = false)
	{
		m_fMouseForceUpdate = bForce;
	}

	BEGIN_MSG_MAP(CMouseHover)
		MESSAGE_HANDLER(WM_MOUSEMOVE, OnMouseMove)
		MESSAGE_HANDLER(WM_MOUSELEAVE, OnMouseLeave)
	END_MSG_MAP()

	LRESULT OnMouseMove(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
	{
		T* pT = static_cast<T*>(this);
		if (!m_fMouseOver)
		{
			m_fMouseOver = true;
			pT->SendMessage(WM_MOUSEENTER, wParam, lParam);
			if (m_fMouseForceUpdate)
			{
				pT->Invalidate();
				pT->UpdateWindow();
			}
			_StartTrackMouseLeave(pT->m_hWnd);
		}
		bHandled = FALSE;
		return 0;
	}
	LRESULT OnMouseLeave(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled)
	{
		T* pT = static_cast<T*>(this);
		if (m_fMouseOver)
		{
			m_fMouseOver = false;
			if (m_fMouseForceUpdate)
			{
				pT->Invalidate();
				pT->UpdateWindow();
			}
		}
		bHandled = FALSE;
		return 0;
	}
	BOOL _StartTrackMouseLeave(HWND hWnd) const
	{
		ATLASSERT(::IsWindow(hWnd));
		TRACKMOUSEEVENT tme = {0};
		tme.cbSize = sizeof(tme);
		tme.dwFlags = TME_LEAVE;
		tme.hwndTrack = hWnd;
		return _TrackMouseEvent(&tme);
	}
	BOOL _CancelTrackMouseLeave(HWND hWnd) const
	{
		TRACKMOUSEEVENT tme = {0};
		tme.cbSize = sizeof(tme);
		tme.dwFlags = TME_LEAVE | TME_CANCEL;
		tme.hwndTrack = hWnd;
		return _TrackMouseEvent(&tme);
	}
};

#endif // NOTRACKMOUSEEVENT


}; // namespace WTL

#endif // __ATLGDIX_H__
